package tech.mhuang.pacebox.core.util;

import tech.mhuang.pacebox.core.io.IOUtil;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * 执行cmd命令工具
 *
 * @author yuanhang.huang
 * @since 1.0.9
 */
public class ExecuteCmdUtil {

    /**
     * 当前系统
     */
    public static final String OS_NAME = System.getProperty("os.name");

    /**
     * win cmd 设置语言为
     */
    public static final String WIN_CMD_CHCP_EN = "chcp 437";

    /**
     * cmd not found 的关键字map
     */
    public static final Map<String, String> CMD_NOT_FOUND_MAP = new HashMap<>();

    /**
     * cmd not found 的关键字
     */
    public static final String CMD_NOT_FOUND = "command not found";

    static {
        CMD_NOT_FOUND_MAP.put("window", "is not recognized as an internal or external command");
        CMD_NOT_FOUND_MAP.put("linux", "command not found");
    }

    /**
     * 执行简单的命令
     * @param cmd   命令
     * @return  是否执行成功
     * @throws IOException IOException
     * @throws InterruptedException InterruptedException
     */
    public static boolean executeCmdSingle(String cmd) throws IOException,InterruptedException {
        Runtime rt = Runtime.getRuntime();
        Process ps = rt.exec(cmd);
        processOutput(ps);
        processErrOutput(ps);
        return processWaitForAndDestroy(ps);
    }

    /**
     * 执行命令，返回执行的线程
     * @param cmd   命令
     * @return  Process
     * @throws IOException IOException
     */
    public static Process executeCmdProcess(String cmd) throws IOException {
        Runtime rt = Runtime.getRuntime();
        return rt.exec(cmd);
    }

    /**
     * 执行线程输出的信息
     * @param ps    执行线程
     * @return  String 线程输出的信息
     * @throws IOException IOException
     */
    public static String processOutput(Process ps) throws IOException {
        InputStream is = ps.getInputStream();
        return readProcessInputStream(is);
    }

    /**
     * 执行线程输出的错误信息
     * @param ps    执行线程
     * @return String 线程输出的错误信息
     * @throws IOException IOException
     */
    public static String processErrOutput(Process ps) throws IOException {
        InputStream err = ps.getErrorStream();
        return readProcessInputStream(err);
    }

    /**
     * 向执行线程中输入信息
     * @param ps    执行线程
     * @param input 输入命令
     * @throws IOException IOException
     */
    public static void processInput(Process ps, String input) throws IOException {
        OutputStream os = ps.getOutputStream();
        IOUtil.write(input, os);
    }

    /**
     * 等待执行线程完毕并返回状态，执行失败自动关闭线程
     * @param ps    执行线程
     * @return  线程执行是否成功
     * @throws InterruptedException InterruptedException
     */
    public static boolean processWaitForAndDestroy(Process ps) throws InterruptedException {
        if(processWaitFor(ps)) {
            return true;
        } else {
            ps.destroy();
            return false;
        }
    }

    /**
     * 等待执行线程完毕并返回状态
     * @param ps    执行线程
     * @return  线程执行是否成功
     * @throws InterruptedException InterruptedException
     */
    public static boolean processWaitFor(Process ps) throws InterruptedException {
        return ps.waitFor() == 0 ? true : false;
    }

    /**
     * 读取执行线程的流数据
     * @param is    输入流
     * @return  String 输入流中的数据字符串
     * @throws IOException IOException
     */
    private static String readProcessInputStream(InputStream is) throws IOException {
        try(BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
            StringBuffer sb = new StringBuffer();
            String line;
            while ((line = br.readLine()) != null) { sb.append(line).append("\n"); }
            return sb.toString();
        }
    }

    /**
     * 检查命令输出的打印中是否有not found
     * @param print 字符串
     */
    public static void checkPrintNotFound(String print) {
        if(print.contains(CMD_NOT_FOUND_MAP.get(OS_NAME)) || print.contains(CMD_NOT_FOUND)) {
            throw new RuntimeException("cmd not found");
        }
    }

}
